package cn.com.hhrcw.web;

import cn.com.hhrcw.constant.CodeVConstant;
import cn.com.hhrcw.params.MobileParams;
import cn.com.hhrcw.sms.entity.SysSmsTemplate;
import cn.com.hhrcw.sms.service.ISysSmsTemplateService;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.IPUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ui.ModelMap;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 短信发送
 *
 * @author guojinfa
 * @since 2020-11-28
 */
@RestController
@Slf4j
@RequestMapping("/person/sms")
@Validated
public class SysSmsTemplateController extends JeecgController<SysSmsTemplate, ISysSmsTemplateService> {
    public static final String VAILD_KEY = "create_sms_token_vaild_";

    @Value("${spring.profiles.active}")
    private String profiles;

    /**
     * 压测环境
     */
    private static final String PROD = "prod";

    @Value("${jeecg.sms.secret}")
    private String secret;

    @Value("${fake.sms.code}")
    private String fakeSmsCode;

    @Value("${fake.sms.phone}")
    private String fakeSmsPhone;

    /**
     * 发送短信验证码
     *
     * @param
     * @return
     */
    @PostMapping("/send")
    public Result<Object> sendSms(@RequestBody MobileParams params, HttpServletRequest request) {

        String token = params.getToken();
        String mobile = params.getMobile();
        String sign = params.getSign();

        if (!StrUtil.equals(profiles, PROD)) {
            redisTemplate.opsForValue().set("SEND_VERIFY_CODE_PERSON_:" + mobile, fakeSmsCode, 5, TimeUnit.MINUTES);
            return Result.ok();
        }
        if (StrUtil.contains(this.fakeSmsPhone, mobile)) {
            log.warn("{}造假验证码为：{}", mobile, this.fakeSmsCode);
            redisTemplate.opsForValue().set("SEND_VERIFY_CODE_PERSON_:" + mobile, fakeSmsCode, 5, TimeUnit.MINUTES);
            return Result.ok();
        }
        List<DictModel> dictModels = sysBaseApi.queryDictItemsByCode("SMSCODE_USER_KEY_CODE");
        Map<String, String> dictMap = dictModels.stream()
            .collect(Collectors.toMap(DictModel::getText, v -> StrUtil.isBlank(v.getValue()) ? "" : v.getValue()));
        String comId = dictMap.get("SMSCODE_USER_CODE");
        String dayPhone = dictMap.get("SMSCODE_USER_DAY_CODE");
        String dayIp = dictMap.get("SMSCODE_IP_DAY_CODE");
        String hourPhone = dictMap.get("SMSCODE_USER_HOUR_CODE");

        String s = DigestUtils.md5Hex(token + mobile + secret);
        if (!s.equals(sign)){
            return Result.error("短信token已过期，请重新生成");
        }
        String addr = IPUtils.getIpAddr(request);
        String key = CodeVConstant.SMSCODE_USER_CODE + mobile;
        String ipKey = CodeVConstant.SMSCODE_IP_DAY_CODE + addr + DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN);
        Boolean hasKey = redisTemplate.hasKey(key);
        //1分钟之内多次调用限制
        Assert.isFalse(hasKey, "短信验证码操作过于频繁");
        //每小时获取验证码次数
        String keyHour = CodeVConstant.SMSCODE_USER_HOUR_CODE + mobile + DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN);
        Integer countHour = (Integer) redisTemplate.opsForValue().get(keyHour);
        log.info("===一小时验证码的次数====" + countHour);
        if (StringUtils.isEmpty(hourPhone)) {
            hourPhone = "5";
        }
        Assert.isFalse(countHour != null && countHour > Long.valueOf(hourPhone), "1小时内短信验证码操作过于频繁");

        //每天获取验证码次数
        String keyDay = CodeVConstant.SMSCODE_USER_DAY_CODE + mobile + DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN);
        log.info("====每日上限次数的key===个人端=" + keyDay);
        Integer stock = (Integer) redisTemplate.opsForValue().get(keyDay);
        log.info("====每日的实际次数====" + stock);
        log.info("====Integer.valueOf(dayPhone)是什么=====" + Integer.valueOf(dayPhone));
        boolean b1 = StrUtil.isNotBlank(dayPhone) && stock != null && stock > Integer.valueOf(dayPhone);
        Assert.isFalse(b1, "今天短信验证码操作过于频繁");

        Integer ipStock = (Integer) redisTemplate.opsForValue().get(ipKey);
        boolean b2 = StrUtil.isNotBlank(dayIp) && ipStock != null && ipStock > Integer.valueOf(dayIp);
        Assert.isFalse(b2, "短信验证码操作过于频繁");

        String mobileKey = VAILD_KEY + mobile;
        Boolean exists = redisTemplate.hasKey(mobileKey);
        //已过期
        Assert.isTrue(exists, "短信发送权限token已过期，请重新生成");
        String orignToken = (String) redisTemplate.opsForValue().get(mobileKey);
        Assert.isTrue(token.equals(orignToken), "短信发送权限token已过期，请重新生成");

        String[] mobile1 = new String[]{mobile};
        boolean b = service.sendSms("person", mobile1);
        if (b) {
            //当天时间
            Date date = new Date();
            //当天零点
            date = DateUtil.beginOfDay(date);
            //第二天零点
            date = DateUtil.offsetDay(date, 1);

            //一小时过期
            redisTemplate.opsForValue().increment(keyHour, 1);
            redisTemplate.expire(keyHour, 1, TimeUnit.HOURS);

            redisTemplate.opsForValue().increment(keyDay, 1);
            redisTemplate.opsForValue().increment(ipKey, 1);
            redisTemplate.expireAt(keyDay, date);
            redisTemplate.expireAt(ipKey, date);
            Long valueOf = Convert.toLong(comId);
            log.info("=======現在時間=========" + valueOf);
            redisTemplate.opsForValue().set(key, mobile, Long.valueOf(comId), TimeUnit.MINUTES);
            return Result.ok();
        }
        return Result.error("发送失败");
    }

    /**
     * 生成发短信令牌
     */
    @PostMapping(value = "/create_token.thtml")
    @ResponseBody
    public Result<Object> createSmsToken(@RequestBody ModelMap requestBody, HttpServletRequest request,
                                         HttpServletResponse response) {
        Map<String, Object> ret = new LinkedHashMap<>();
        int length = 11;
        try {
            String phone = (String) requestBody.get("phone");
            if (StringUtils.isBlank(phone)) {
                return Result.error("请输入手机号！");
            } else if (phone.length() != length) {
                return Result.error("请输入正确的手机号！");
            }
            String token = IdUtil.getSnowflake(0, 0).nextIdStr();

            //限制IP生成令牌  10秒
            String ip = IPUtils.getIpAddr(request);
            log.info("===ip多少：====" + ip);
            String key = "create_sms_token_limit_" + ip;
            log.info("===查看这个ipKey对应的值====" + redisTemplate.opsForValue().get(key));
            Assert.isFalse(redisTemplate.hasKey(key), "本机操作太频繁，稍后再试！");
            String ipKey = "1";
            List<DictModel> dictModels = sysBaseApi.queryDictItemsByCode("SMSCODE_USER_KEY_CODE");
            log.info("======dictModels=========" + dictModels);
            for (DictModel model : dictModels) {
                boolean equals = "CREATE_TOKEN_IP".equals(model.getText());
                if (equals) {
                    ipKey = model.getValue();
                }
            }
            log.info("======限制的key======" + key);
            //限制加入缓存；//多少秒过期
            redisTemplate.opsForValue().set(key, token, Long.valueOf(ipKey), TimeUnit.SECONDS);
            //目前是一秒上面这个就过期了
            log.info("=====限制=======" + Long.valueOf(ipKey));

            //验证令牌加入缓存
            String phoneKey = VAILD_KEY + phone;
            log.info("===验证令牌===" + phoneKey);
            redisTemplate.opsForValue().set(phoneKey, token, 5, TimeUnit.MINUTES);

            ret.put("token", token);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
            return Result.error("");
        }
        return Result.ok(ret);

    }
}
